package com.lagou.edu.factory;


import com.lagou.annocation.*;
import com.lagou.edu.config.SpringConfig;
import com.lagou.edu.utils.TransactionManager;
import com.mysql.jdbc.StringUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

public class AnnoBeanFactory {
    //首先需要提供配置类--指定扫描的包
    //遍历包下面的所有文件,取出贴了注解的文件放到一个容器里面
    //取出的过程中进行处理别名 和 类型
    //进行处理依赖
    //进行代理类的处理

    private static Class configClass = SpringConfig.class;

    private static Set<String> classNames = new HashSet<>();
    /**
     * 存放对象的容器
     */
    private static Map<String,Object>  singletonObjects = new HashMap<>();
    /**
     * 进行映射名字
     */
    private static Map<String,String> nameAlias = new HashMap<>();
    /**
     * 进行映射类型
     */
    private static Map<Class,String> typeAlias = new HashMap<>();
    static{
        //项目的跟路径
        String path = AnnoBeanFactory.class.getResource("/").getPath();
        ComponmentScan componmentScan = (ComponmentScan)configClass.getAnnotation(ComponmentScan.class);
        String basePackage = componmentScan.basePackage();
        if(StringUtils.isNullOrEmpty(basePackage)){
            basePackage = configClass.getPackageName();
        }
        String packagePath = basePackage.replaceAll("\\.","\\/");
        path = path + packagePath;
        File file = new File(path);
        resolveClasses(file,basePackage,classNames);
        //进行处理包下面的类
        for (String className : classNames) {
            if(!className.contains("BeanFactory")){
                resolveClassName(className);
            }
        }
        //处理依赖
        resolveDependies(singletonObjects);
        //进行处理代理对象
        resolveProxy(singletonObjects);




    }

    /**
     * 进行处理代理对象的方法
     * @param singletonObjects
     */
    private static void resolveProxy(Map<String, Object> singletonObjects) {
        Set<String> keySet = singletonObjects.keySet();
        for (String key : keySet) {
            Object obj = singletonObjects.get(key);
            Service service = obj.getClass().getAnnotation(Service.class);
            if(service!=null){
                Transactional transactional = obj.getClass().getAnnotation(Transactional.class);
                if(transactional!=null){
                    ProxyFactory proxyFactory = (ProxyFactory) singletonObjects.get("proxyFactory");
                    Class<?>[] interfaces = obj.getClass().getInterfaces();
                    Object proxy;
                    if(interfaces.length>0){
                        proxy = proxyFactory.getJdkProxy(obj);
                    }else{
                        proxy = proxyFactory.getCglibProxy(obj);
                    }
                    singletonObjects.put(key,proxy);
                }
            }
        }
    }

    /**
     * 进行处理类之间的依赖关系
     * @param singletonObjects
     */
    private static void resolveDependies(Map<String, Object> singletonObjects) {
        Collection<Object> values = singletonObjects.values();
        for (Object obj : values) {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                if(autowired!=null){
                    Object fieldValue;
                    String value = autowired.value();
                    if(!StringUtils.isNullOrEmpty(value)){
                        fieldValue = getBean(value);
                    }else{
                        Class<?> type = field.getType();
                        fieldValue = getBean(type);
                    }
                    field.setAccessible(true);
                    try {
                        field.set(obj,fieldValue);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 对对应全限定雷鸣进行处理
     * @param className
     */
    private static void resolveClassName(String className) {
        try {
            Class<?> aClass = Class.forName(className);
            Annotation[] annotations = aClass.getDeclaredAnnotations();
            for (Annotation annotation : annotations) {
                if(annotation instanceof Componment){
                    Componment componment = (Componment) annotation;
                    String value = componment.value();
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if (interfaces.length>0){
                        if (StringUtils.isNullOrEmpty(value)) {
                            String name = interfaces[0].getSimpleName();
                            value = name.substring(0, 1).toLowerCase() + name.substring(1);
                        }
                        //添加上了类型和Id的映射
                        typeAlias.put(interfaces[0],value);
                    }else{
                        if (StringUtils.isNullOrEmpty(value)) {
                            String name = aClass.getSimpleName();
                            value = name.substring(0, 1).toLowerCase() + name.substring(1);
                        }
                        //添加上了类型和Id的映射
                        typeAlias.put(aClass,value);
                    }
                    String[] names = componment.names();
                    for (String name : names) {
                        nameAlias.put(name,value);
                    }
                    Object o = aClass.getDeclaredConstructor().newInstance();
                    singletonObjects.put(value,o);
                    continue;
                }else if(annotation instanceof Service){
                    Service service = (Service) annotation;
                    String value = service.value();
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if (interfaces.length>0){
                        if (StringUtils.isNullOrEmpty(value)) {
                            String name = interfaces[0].getSimpleName();
                            value = name.substring(0, 1).toLowerCase() + name.substring(1);
                        }
                        //添加上了类型和Id的映射
                        typeAlias.put(interfaces[0],value);
                    }else{
                        if (StringUtils.isNullOrEmpty(value)) {
                            String name = aClass.getSimpleName();
                            value = name.substring(0, 1).toLowerCase() + name.substring(1);
                        }
                        //添加上了类型和Id的映射
                        typeAlias.put(aClass,value);
                    }
                    String[] names = service.names();
                    for (String name : names) {
                        nameAlias.put(name,value);
                    }
                    Object o = aClass.getDeclaredConstructor().newInstance();
                    singletonObjects.put(value,o);
                    continue;
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 进行获取路径下面的所有类的全限定类名
     * @param file
     * @param parentPackage
     * @param classNames
     */
    private static void resolveClasses(File file,String parentPackage,Set<String> classNames){
        File[] files = file.listFiles();
        for (File childFile : files) {
            String childFileName = childFile.getName();
            if(childFile.isFile() && childFileName.endsWith(".class")){
                String replace = childFileName.replaceAll("\\.class", "");
                classNames.add(parentPackage+"."+ replace);
            }else if(childFile.isDirectory()){
                resolveClasses(childFile,parentPackage+"."+childFileName,classNames);
            }
        }
    }






    /**
     * 通过名字或者id获取对象
     * @param idOrName
     * @param <E>
     * @return
     */
    public static <E> E  getBean(String idOrName){
        Object o = singletonObjects.get(idOrName);
        if(o==null){
            String s = nameAlias.get(idOrName);
            o = singletonObjects.get(s);
        }
        return (E) o;
    }

    /**
     * 通过类型进行获取对象
     * @param typeClass
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class typeClass){
        String s = typeAlias.get(typeClass);
        Object o = singletonObjects.get(s);
        return (T) o;
    }

}
